import os
import time
from datetime import datetime
import allure
from selenium.common.exceptions import JavascriptException
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webdriver import WebDriver
from libraries.logger import log
from configs.settings import SCREENSHOT


class Base:
    '''页面action基础封装

        loc: 元素定位表达式（推荐tuple）
        desc: action描述
        timeout: 超时时间
        frequency: 轮询频率
    '''

    def __init__(self, driver: WebDriver):
        self.driver = driver

    def __save_screenshot(self, desc):
        '''页面截屏保存截图'''
        file_name = os.path.join(SCREENSHOT, f"{datetime.strftime(datetime.now(), '%Y%m%d%H%M%S%f')}_{desc}.png")
        self.driver.save_screenshot(file_name)
        with open(file_name, mode='rb') as f:
            file = f.read()
        allure.attach(file, desc, allure.attachment_type.PNG)
        log.warning(f"页面截图文件存储路径：{file_name}")

    def __get_element(self, loc, desc):
        '''获取元素'''
        try:
            ele = self.driver.find_element(*loc)
        except Exception as e:
            log.error(f"获取元素失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.debug(f"成功获取元素，loc：{loc}")
            return ele

    def __get_elements(self, loc, desc):
        '''获取元素组'''
        try:
            ele = self.driver.find_elements(*loc)
        except Exception as e:
            log.error(f"获取元素组失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.debug(f"成功获取元素组，loc：{loc}")
            return ele

    def go_to(self, url):
        """当前页面跳转到新的链接地址"""
        log.info(f"页面切换：{self.driver.current_url}—>{url}")
        self.driver.execute_script(f"window.location='{url}'")
        time.sleep(2)
        log.info(f"当前 URL：{url}")

    def __import_jquery(self):
        """导入jquery包"""
        jq = """
                var importJs=document.createElement('script');
                importJs.setAttribute("type","text/javascript");
                importJs.setAttribute("src", 'https://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js');
                document.getElementsByTagName("head")[0].appendChild(importJs);
                """
        log.info(f"导入jquery包：{jq}")
        self.driver.execute_script(jq)

    def execute_javascript(self, js, desc=None, *args):
        """执行javascript/jquery"""
        code_type = 'javascript'
        if '$' in js:
            self.__import_jquery()
            code_type = 'jquery'
        time.sleep(2)
        log.info(f"action：{desc} 执行{code_type}：{js}")
        try:
            return self.driver.execute_script(js, *args)
        except JavascriptException as exc:
            log.info(f"selenium.common.exceptions.JavascriptException: {exc}")

    @staticmethod
    def ignore_error(func, *args, **kwargs):
        """忽略异常"""
        try:
            log.info(f"执行方法：{func.__name__}，异常时忽略")
            res = func(*args, **kwargs)
            return res
        except Exception as exc:
            log.warning(f"忽略异常：{exc}")
            return False

    def wait_element_to_be_visible(self, loc, desc, timeout=20, frequency=0.5):
        '''等待元素可见'''
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.visibility_of_element_located(loc))
        except Exception as e:
            log.error(f"元素可见等待失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.debug(f"等待元素可见，loc：{loc}，等待时间：{round(end_time - start_time, 2)}秒")

    def wait_element_visible_and_return_status(self, loc, timeout=20, frequency=0.5):
        """等待元素可见并返回元素可见状态"""
        try:
            WebDriverWait(self.driver, timeout, frequency).until(EC.visibility_of_element_located(loc))
        except Exception:
            log.error(f"元素可见等待失败，loc：{loc}，返回状态：False")
            return False
        else:
            log.debug(f"等待元素可见，loc：{loc}，，返回状态：True")
            return True

    def wait_element_exists_and_return_status(self, loc, timeout=20, frequency=0.5):
        """等待元素存在并返回元素可见状态"""
        try:
            WebDriverWait(self.driver, timeout, frequency).until(EC.presence_of_element_located(loc))
        except Exception:
            log.error(f"元素存在等待失败，loc：{loc}，返回状态：False")
            return False
        else:
            log.debug(f"等待元素存在，loc：{loc}，，返回状态：True")
            return True

    def wait_element_not_visible_and_return_status(self, loc, timeout=20, frequency=0.5):
        """等待元素不存在并返回元素可见状态"""
        try:
            WebDriverWait(self.driver, timeout, frequency).until_not(EC.visibility_of_element_located(loc))
        except Exception:
            log.error(f"元素存在等待失败，loc：{loc}，返回状态：False")
            return True
        else:
            log.debug(f"等待元素存在，loc：{loc}，，返回状态：True")
            return False

    def wait_element_to_be_click(self, loc, desc, timeout=20, frequency=0.5):
        '''等待元素可点击'''
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.element_to_be_clickable(loc))
        except Exception as e:
            log.error(f"元素可点击等待失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.debug(f"等待元素可点击，loc：{loc}，等待时间：{round(end_time - start_time, 2)}秒")

    def wait_element_to_be_exist(self, loc, desc, timeout=20, frequency=0.5):
        '''等待元素存在'''
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.presence_of_element_located(loc))
        except Exception as e:
            log.error(f"元素存在等待失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.debug(f"等待元素存在，loc：{loc}，等待时间：{round(end_time - start_time, 2)}秒")

    def wait_element_not_to_be_visible(self, loc, desc, timeout=60, frequency=0.5):
        '''等待元素不出现'''
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until_not(EC.visibility_of_element_located(loc))
        except Exception as e:
            log.error(f"元素不可见等待失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.info(f"等待元素不可见成功，loc：{loc}，等待时间：{round(end_time - start_time, 2)}秒")

    def wait_element_not_to_be_exists(self, loc, desc, timeout=10, frequency=0.5):
        '''等待元素不存在'''
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until_not(EC.presence_of_element_located(loc))
        except Exception as e:
            log.error(f"元素不存在等待失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.info(f"等待元素不存在成功，loc：{loc}，等待时间：{round(end_time - start_time, 2)}秒")

    def wait_title_contains(self, title, timeout=30, frequency=0.5):
        """等待给定的部分title被包含"""
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.title_contains(title))
        except Exception:
            log.error(f"等待当前Title包含{title}失败，当前title：{self.driver.title}")
            return False
        else:
            end_time = time.time()
            log.info(f"等待当前Title包含{title}成功，当前title：{self.driver.title}，等待时间：{round(end_time - start_time, 2)}秒")
            return True

    def wait_url_contains(self, part_url, timeout=30, frequency=0.5):
        """等待给定的部分Url被包含"""
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.url_contains(part_url))
        except Exception:
            log.error(f"等待当前URL包含{part_url}失败, current_url：{self.driver.current_url}")
            return False
        else:
            end_time = time.time()
            log.info(f"等待当前URL包含{part_url}成功, current_url：{self.driver.current_url}，等待时间：{round(end_time - start_time, 2)}秒")
            return True

    def input_text(self, text, loc, desc, timeout=20, frequency=0.5):
        '''输入文本内容

            text: 输入的文本内容
        '''
        try:
            self.wait_element_to_be_click(loc, desc, timeout, frequency)
            self.__get_element(loc, desc).send_keys(text)
        except Exception as e:
            log.error(f"action：{desc}，输入文本内容（{text}）失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，成功输入文本内容（{text}），loc：{loc}")

        return self

    def input_text_after_clear(self, text, loc, desc, timeout=20, frequency=0.5):
        '''在清除文本之后输入文本内容

            text: 输入的文本内容
        '''
        try:
            self.wait_element_to_be_click(loc, desc, timeout, frequency)
            self.__get_element(loc, desc).clear()
            self.__get_element(loc, desc).send_keys(text)
        except Exception as e:
            log.error(f"action：{desc}，输入文本内容（{text}）失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，成功输入文本内容（{text}），loc：{loc}")

        return self

    def clear_text(self, loc, desc, timeout=20, frequency=0.5):
        '''清空文本框'''
        try:
            self.wait_element_to_be_click(loc, desc, timeout, frequency)
            self.__get_element(loc, desc).clear()
        except Exception as e:
            log.error(f"action：{desc}，清空文本框失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，成功清空文本框内容，loc：{loc}")

        return self

    def click_button(self, loc, desc, timeout=20, frequency=0.5):
        '''点击按钮'''
        try:
            self.wait_element_to_be_visible(loc, desc, timeout, frequency)
            self.__get_element(loc, desc).click()
        except Exception as e:
            log.error(f"action：{desc}，点击按钮失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，成功点击按钮，loc：{loc}")

        return self

    def scroll_into_view(self, loc, desc, timeout=20, frequency=0.5):
        '''滚动到指定元素位置'''
        try:
            self.wait_element_to_be_exist(loc, desc, timeout, frequency)
            self.__get_element(loc, desc).location_once_scrolled_into_view
        except Exception as e:
            log.error(f"action：{desc}，滚动到指定元素失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，成功滚动到指定元素，loc：{loc}")

        return self

    def get_element_text(self, loc, desc, timeout=20, frequency=0.5):
        '''获取元素的文本值'''
        try:
            self.wait_element_to_be_exist(loc, desc, timeout, frequency)
            text = self.__get_element(loc, desc).text
        except Exception as e:
            log.error(f"action：{desc}，获取文本失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，获取到的元素文本值为：{text}，loc：{loc}")
            return text

    def get_elements_text(self, loc, desc, timeout=20, frequency=0.5):
        '''获取元素的所有文本值'''
        try:
            self.wait_element_to_be_visible(loc, desc, timeout, frequency)
            all_obj = self.__get_elements(loc, desc)
            text_list = [ele_obj.text for ele_obj in all_obj]
        except Exception as e:
            log.error(f"action：{desc}，获取文本列表失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，获取到的元素文本值列表为：{text_list}，loc：{loc}")
            return text_list

    def get_elements_length(self, loc, desc, timeout=20, frequency=0.5):
        '''获取所有元素并返回长度'''
        try:
            self.wait_element_to_be_exist(loc, desc, timeout, frequency)
            length = len(self.__get_elements(loc, desc))
        except Exception as e:
            log.error(f"action：{desc}，获取所有元素个数失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，获取该元素所有对象个数：{length}，loc：{loc}")
            return length

    def get_element_attr(self, attr_name, loc, desc, timeout=20, frequency=0.5):
        '''获取元素的属性值

            attr_name: 属性名称
        '''
        try:
            self.wait_element_to_be_exist(loc, desc, timeout, frequency)
            value = self.__get_element(loc, desc).get_attribute(attr_name)
        except Exception as e:
            log.error(f"action：{desc}，获取元素的属性值{attr_name}失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，获取到元素属性{attr_name}的值为：{value}，loc：{loc}")
            return value

    def get_elements_attr(self, attr_name, loc, desc, timeout=20, frequency=0.5):
        '''获取元素的所有属性值

            attr_name: 属性名称
        '''
        try:
            self.wait_element_to_be_exist(loc, desc, timeout, frequency)
            all_obj = self.__get_elements(loc, desc)
            value_list = [ele_obj.get_attribute(attr_name) for ele_obj in all_obj]
        except Exception as e:
            log.error(f"action：{desc}，获取元素的所有属性值{attr_name}失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，获取到元素属性{attr_name}的列表值为：{value_list}，loc：{loc}")
            return value_list

    def switch_to_frame(self, loc, desc, timeout=20, frequency=0.5):
        '''iframe页面切换'''
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.frame_to_be_available_and_switch_to_it(loc))
        except Exception as e:
            log.error(f"action：{desc}，iframe切换失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.info(f"action：{desc}，iframe切换成功，loc：{loc}，等待时间：{round(end_time - start_time, 2)}秒")

        return self

    def switch_to_default_content(self, desc):
        '''切换iframe到main页面'''
        try:

            self.driver.switch_to.default_content()
        except Exception as e:
            log.error("切换iframe到main页面失败")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info("成功切换iframe到main页面")

        return self

    def switch_to_parent(self, desc):
        '''切换iframe到上一层页面'''
        try:
            self.driver.switch_to.parent_frame()
        except Exception as e:
            log.error("切换iframe到上一层页面失败")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info("成功切换iframe到上一层页面")

        return self

    def suspend_mouse(self, loc, desc, timeout=20, frequency=0.5):
        '''鼠标悬浮'''
        try:
            self.wait_element_to_be_visible(loc, desc, timeout, frequency)
            ele = self.__get_element(loc, desc)
            ActionChains(self.driver).move_to_element(ele).perform()
        except Exception as e:
            log.error(f"action：{desc}，鼠标悬浮失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，成功鼠标悬浮，loc：{loc}")

        return self

    def alert_close(self, desc, alert_type='alert', text=None, timeout=20, frequency=0.5):
        '''弹框关闭

            alert_type: 弹框类型（alert/confirm/prompt）
            text: prompt弹框输入的文本
        '''
        try:
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.alert_is_present())
            if alert_type in ['alert', 'confirm']:
                self.driver.switch_to.alert.accept()
            elif alert_type == 'prompt':
                self.driver.switch_to.alert.send_keys(text)
                self.driver.switch_to.alert.accept()
            else:
                log.error(f"不支持{alert_type},请确认alert的类型")
        except Exception as e:
            log.error(f"action：{desc}，关闭{alert_type}类型的弹框失败")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.info(f"action：{desc}，关闭{alert_type}类型的弹框成功，等待时间：{round(end_time - start_time, 2)}秒")

        return self

    def select_action(self, content, select_type, loc, desc, timeout=20, frequency=0.5):
        '''Select操作

            content: select_by_方法的入参
            select_type: select类型（index/value/text）
        '''
        try:
            self.wait_element_to_be_click(loc, desc, timeout, frequency)
            ele = self.__get_element(loc, desc)
            if select_type == 'index':
                Select(ele).select_by_index(content)
            elif select_type == 'value':
                Select(ele).select_by_value(content)
            elif select_type == 'text':
                Select(ele).select_by_visible_text(content)
            else:
                log.error(f"不支持{select_type},请确认Select的类型")
        except Exception as e:
            log.error(f"action：{desc}，下拉选择失败，选择类型：{select_type}，值: {content}，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            log.info(f"action：{desc}，下拉选择成功，选择类型：{select_type}，值: {content}，loc：{loc}")

        return self

    def switch_to_windows(self, loc, desc, timeout=20, frequency=0.5):
        '''窗口切换'''
        try:
            cur_handles = self.driver.window_handles  # 获取点击之前的窗口总数
            start_time = time.time()
            self.click_button(loc, desc, timeout, frequency)  # 点击按钮后新的窗口出现
            WebDriverWait(self.driver, timeout, frequency).until(EC.new_window_is_opened(cur_handles))
            wins = self.driver.window_handles  # 再次获取窗口总数
            self.driver.switch_to.window(wins[-1])  # 切换到新的页面
        except Exception as e:
            log.error(f"action：{desc}，窗口切换失败，loc：{loc}")
            self.__save_screenshot(desc)
            raise e
        else:
            end_time = time.time()
            log.info(f"action：{desc}，窗口切换成功，等待时间：{round(end_time - start_time, 2)}秒")

        return self
